package com.smart.campus.school.service.impl;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.google.common.collect.Lists;
import com.smart.campus.common.utils.DateUtils;
import com.smart.campus.common.utils.StringUtils;
import com.smart.campus.school.domain.*;
import com.smart.campus.school.domain.vo.AttendanceResultVo;
import com.smart.campus.school.enums.CheckInEnum;
import com.smart.campus.school.mapper.*;
import com.smart.campus.school.service.IAttendanceResultService;
import com.smart.campus.school.service.IClockInRecordService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 考勤组Service业务层处理
 *
 * @author Zhihe.Li
 * @date 2020-10-21
 */
@Service
@Slf4j
public class AttendanceResultServiceImpl implements IAttendanceResultService {
    public static final int LR_NUM = 2;//相同节点数判断
    public static int DEVICES_TM = 1*60;//打卡设备之间的间隔时间
    //    public static int DEVICES_TM_S = 1;//打卡设备之间的间隔时间
    public static int RECORD_TM = 3*60;//打卡记录之间的间隔时间
    @Autowired
    private StudentMapper studentMapper;
    @Autowired
    private AttendanceGroupMapper attendanceGroupMapper;
    @Autowired
    private AttendanceGroupUserMapper attendanceGroupUserMapper;
    @Autowired
    private AttendanceRuleMapper attendanceRuleMapper;
    @Autowired
    private AttendanceRecordMapper attendanceRecordMapper;
    @Autowired
    private ScBadgeMapper scBadgeMapper;

    @Autowired
    private AttendanceRecordValidMapper attendanceRecordValidMapper;

    @Autowired
    IClockInRecordService clockInRecordService;

    @Override
    public List<AttendanceResultVo> selectAttendanceResultList(Student student) {

        Map<Long, AttendanceResultVo> map = saveAttendanceResult(false);

        if (MapUtils.isNotEmpty(map)) {
            return map.values().stream().collect(Collectors.toList());
        }
        List<AttendanceResultVo> attendanceResultVoList = new ArrayList<AttendanceResultVo>();
        //1、查询学生
        List<Student> studentList = this.studentMapper.selectStudentList(student);

        if (studentList != null && studentList.size() > 0) {
            for (Student stu : studentList) {
                AttendanceResultVo attendanceResultVo = new AttendanceResultVo();
                attendanceResultVoList.add(attendanceResultVo);
                attendanceResultVo.setUserNo(stu.getStudentNo());
                attendanceResultVo.setUserName(stu.getStudentName());
                //查询校徽
                ScBadge scBadge = scBadgeMapper.selectScBadgeByUserId(stu.getId());
                if (scBadge == null) {
                    attendanceResultVo.setRemark("没有配发校徽，无法考勤");
                    continue;
                }
                attendanceResultVo.setBadgeNo(scBadge.getBadgeNo());
                //2、查询学生所在的考勤组
                AttendanceGroupUser attendanceGroupUser = this.attendanceGroupUserMapper.selectAttendanceGroupUserByUserId(stu.getId());
                if (attendanceGroupUser == null) {
                    attendanceResultVo.setRemark("没有加入考勤组，无法考勤");
                    continue;
                }

                AttendanceGroup attendanceGroup = this.attendanceGroupMapper.selectAttendanceGroupById(attendanceGroupUser.getGroupId());
                String rulesStr = attendanceGroup.getRuleId();//考勤组配置考勤规则时，需要按照规则的结束时间升序排序，然后拼接成字符串保存
                String[] rules = rulesStr.split(",");

                long lastRuleEnd = 0;
                for (int i = 0; i < rules.length; i++) {
                    //3、查询考勤组对应的规则
                    AttendanceRule rule = this.attendanceRuleMapper.selectAttendanceRuleById(Long.parseLong(rules[i]));
                    // long ruleBegin = Long.valueOf(rule.getBeginTime().replaceAll("[:]",""));
                    LocalTime ruleBegin = LocalTime.parse(rule.getBeginTime());
                    // long ruleEnd = Long.valueOf(rule.getEndTime().replaceAll("[:]",""));
                    LocalTime ruleEnd = LocalTime.parse(rule.getEndTime());

                    //String now = DateUtils.dateTimeNow("HH:mm:ss");
                    LocalTime now = LocalTime.now();
                    //如果当前时间未到考勤开始时间，那么结束计算
                    if (now.isBefore(ruleBegin)) {
                        attendanceResultVo.setRemark((attendanceResultVo.getRemark() != null ? attendanceResultVo.getRemark() : "") + rule.getName() + "未到考勤时间");
                        continue;
                    }

                    //4、查询当日打卡记录：当日所有打卡记录升序排列
                    Map<String, Object> condition = new HashMap<String, Object>();
                    condition.put("cardNo", scBadge.getBadgeNo());//校徽号就是考勤卡号
                    condition.put("createDate", DateUtils.dateTimeNow("yyyy-MM-dd"));
                    List<AttendanceRecord> attendanceRecordList = this.attendanceRecordMapper.selectAttendanceRecordByCardNoCreateDate(condition);
                    if (attendanceRecordList == null || attendanceRecordList.size() == 0) {
                        attendanceResultVo.setRemark((attendanceResultVo.getRemark() != null ? attendanceResultVo.getRemark() : "") + rule.getName() + "缺勤");
                        continue;
                    }
                    //5、打卡记录X与规则进行比对，有以下几种情况
                    Date minRecordTime = attendanceRecordList.get(0).getCreateTime();

                    //提取离这个班次最近的考勤记录
                    Long minRecord = null;
                    for (AttendanceRecord attendanceRecord : attendanceRecordList) {
                        long record = Long.valueOf(DateUtils.parseDateToStr("HH:mm:ss", attendanceRecord.getCreateTime()).replaceAll("[:]", ""));
                        if (record > lastRuleEnd) {
                            minRecord = record;
                            break;
                        }
                    }

                   /* if( minRecord==null){ //5.1、如果 X不存在，那么早上缺勤
                        attendanceResultVo.setRemark((attendanceResultVo.getRemark()!=null?attendanceResultVo.getRemark():"")+rule.getName() + "缺勤");
                    }else if( lastRuleEnd < minRecord && minRecord <  ruleBegin){ //5.1、如果 最小值X < 早上上班，那么早上正常
                        attendanceResultVo.setRemark((attendanceResultVo.getRemark()!=null?attendanceResultVo.getRemark():"")+rule.getName() + "正常");
                    }else if(ruleBegin < minRecord &&  minRecord < ruleEnd){//5.2 早上上班<最小值X<早上下班，那么早上迟到
                        attendanceResultVo.setRemark((attendanceResultVo.getRemark()!=null?attendanceResultVo.getRemark():"")+rule.getName() + "迟到");
                    }else if( minRecord > ruleEnd ){  //5.3 最小值X>早上下班，那么早上缺勤
                        attendanceResultVo.setRemark((attendanceResultVo.getRemark()!=null?attendanceResultVo.getRemark():"")+rule.getName() + "缺勤");
                    }*/
                    lastRuleEnd = Long.valueOf(rule.getEndTime().replaceAll("[:]", ""));


                    //5.4 早上下班<存在X<下午上班，那么下午正常
                    //5.5 下午上班<存在X<下午下班，那么下午迟到
                    //5.6 最小值X>下午下班 或者 X不存在，那么下午缺勤
                }
            }
        }
        return attendanceResultVoList;
    }

    /**
     * 保存打卡记录
     */
    @Override
    public Map<Long, AttendanceResultVo> saveAttendanceResult(boolean save) {
        //以班级为维度查询学生
        Long maxId = 0L;
        Integer pageSize = 100;
        int count = 0;
        Map<Long, AttendanceResultVo> resultVoMap = new HashMap<>();
        //查询所有考勤组
        List<AttendanceGroup> groupList = attendanceGroupMapper.selectAttendanceGroupList(new AttendanceGroup());
        //查询所有考勤规则
        List<AttendanceRule> ruleList = attendanceRuleMapper.selectAttendanceRuleList(new AttendanceRule());

        if (CollectionUtils.isNotEmpty(groupList) && CollectionUtils.isNotEmpty(ruleList)) {
            //把考勤组转成map
            Map<Long, AttendanceGroup> groupMap = groupList.stream().collect(Collectors.toMap(AttendanceGroup::getId, a -> a, (k1, k2) -> k1));
            //把考勤规则转成map
            Map<Long, AttendanceRule> ruleMap = ruleList.stream().collect(Collectors.toMap(AttendanceRule::getId, a -> a, (k1, k2) -> k1));
            while (true) {
                List<Student> studentList = this.studentMapper.getStudentByMaxId(maxId, pageSize);
                if (CollectionUtils.isEmpty(studentList)) {
                    return resultVoMap;
                }
                for (Student student : studentList) {
                    AttendanceResultVo attendanceResultVo = new AttendanceResultVo();
                    attendanceResultVo.setUserNo(student.getStudentNo());
                    attendanceResultVo.setSchoolId(student.getSchoolId());
                    attendanceResultVo.setUserName(student.getStudentName());
                    resultVoMap.put(student.getId(), attendanceResultVo);
                }
                count++;
                maxId = studentList.get(studentList.size() - 1).getId();
                log.info("最大id:{},处理次数:{}", maxId, count);
                List<Long> studentIdList = studentList.stream().map(student -> student.getId()).collect(Collectors.toList());
                //查询校徽
                List<ScBadge> scBadgeList = scBadgeMapper.selectScBadgeListByStudentIdList(studentIdList);

                if (CollectionUtils.isNotEmpty(scBadgeList)) {
                    //查询考勤组人员
                    List<AttendanceGroupUser> list = attendanceGroupUserMapper.selectAttendanceGroupUserListByUserIdList(studentIdList);
                    List<String> badgeNoList = scBadgeList.stream().map(badge -> badge.getBadgeNo()).collect(Collectors.toList());

                    Map<Long, List<ScBadge>> cardNoMap = scBadgeList.stream().collect(Collectors.groupingBy(ScBadge::getUserId));
                    //获取打卡记录
                    Map<String, Object> condition = new HashMap<String, Object>();
                    badgeNoList.add("0972226736");
                    condition.put("cardNoList", badgeNoList);//校徽号就是考勤卡号
                    //todo 记得改成当天
                    String dnow = DateUtils.dateTimeNow("yyyy-MM-dd");
                    dnow = "2020-12-04";
                    condition.put("createDate", dnow);
                    String l1 = DateUtils.parseDateToStr("yyyyMMdd", DateUtils.parseDate(dnow));
                    long ymd = Long.parseLong(l1);
                    AttendanceRecordValid tmp = new AttendanceRecordValid();
                    tmp.setYmd(ymd);
                    attendanceRecordValidMapper.deleteAttendanceRecordValidByObj(tmp);
                    List<AttendanceRecord> attendanceRecordList = this.attendanceRecordMapper.selectAttendanceRecordByCardNoListCreateDate(condition);
                    //按卡号分组
                    Map<String, List<AttendanceRecord>> recordMap = attendanceRecordList.stream().collect(Collectors.groupingBy(AttendanceRecord::getCardNo));
                    //TODO 第一次清洗数据，将符合一出一入或者一入一出的有效打卡数据，存入到有效记录表中
                    for (AttendanceGroupUser attendanceGroupUser : list) {
                        List<ScBadge> badgeList = cardNoMap.get(attendanceGroupUser.getUserId());
                        //1、获取校徽编号
                        String cardNo = "";
                        ScBadge scBadge = null;
                        if (CollectionUtils.isNotEmpty(badgeList)) {
                            scBadge = badgeList.get(0);
                            cardNo = scBadge.getBadgeNo();
                        }
//                        cardNo = "0972226736";
                        //2、如果校徽编号不为空，则获取打卡记录，做数据清洗，将有效打卡记录写入记录表中
                        if (StringUtils.isNotEmpty(cardNo)) {
                            List<AttendanceRecord> recordList = recordMap.get(cardNo);
                            //如果打卡记录存在，按照打卡记录计算，否则不处理
                            if (CollectionUtils.isNotEmpty(recordList)) {
                                recordList = recordList.stream().sorted(Comparator.comparing(AttendanceRecord::getCreateTime)).collect(Collectors.toList());
                                AttendanceRecord nearNode = null;
                                Date nearTime = null;//最近一次打卡时间
                                int lastRecordType = -1;
                                for (int i = 0; i < recordList.size(); i++) {
                                    //如果设备Id相同，或者设备类型相同，则覆盖最近打卡记录
                                    if (nearNode == null) {
                                        nearNode = recordList.get(i);
                                        continue;
                                    }
                                    boolean bCome = isCome(nearNode.getDeviceNo());
                                    if (lastRecordType > -1) {
                                        //上一条的记录状态，如果是入，0，则bCome不可以是true
                                        if (lastRecordType == 0 && bCome) {
                                            nearNode = null;
                                            continue;
                                        }
                                        if (lastRecordType == 1 & !bCome) {
                                            nearNode = null;
                                            continue;
                                        }
                                    }
                                    AttendanceRecord node = recordList.get(i);
                                    if (nearTime != null) {
                                        //有过最近的打卡记录，需要去掉最近{设定的}时间端（系统默认5分钟）内的打卡记录
                                        long minute = DateUtil.between(node.getCreateTime(), nearTime, DateUnit.MINUTE);
                                        if (minute < RECORD_TM) {
                                            continue;
                                        } else {
                                            nearNode = node;
                                            nearTime = null;
                                            continue;
                                        }
                                    }


                                    boolean bComeNode = isCome(node.getDeviceNo());

                                    if (bCome == bComeNode) {
                                        nearNode = node;
                                    } else {
                                        //不是同一类型设备
                                        Date d1 = node.getCreateTime();
                                        Date d2 = nearNode.getCreateTime();
                                        long secc = DateUtil.between(d1, d2, DateUnit.SECOND);
                                        if (secc == 0) {
                                            //过滤掉打卡时间1S都不差的数据
                                            continue;
                                        }
                                        //2个打卡时间相差的分钟数，如果超过2分钟，重启一次有效记录
                                        long minute = DateUtil.between(d1, d2, DateUnit.MINUTE);

                                        if (minute < DEVICES_TM&&minute>0) {
                                            //2分钟以内，算一次有效数据 从入门打卡点到另一个非同类别打卡点
                                            AttendanceRecordValid aValid = new AttendanceRecordValid();
                                            aValid.setCardNo(cardNo);
                                            aValid.setHpdate(d2);
                                            aValid.setClockIn1(DateUtils.getHM(nearNode.getCreateTime()));
                                            aValid.setDeviceNo1(nearNode.getDeviceNo());
                                            aValid.setDeviceNo2(node.getDeviceNo());
                                            aValid.setClockIn2(DateUtils.getHM(node.getCreateTime()));
                                            aValid.setSchoolId(node.getSchoolId());
                                            lastRecordType = bComeNode ? CheckInEnum.OUT.getType() : CheckInEnum.IN.getType();
                                            aValid.setInOut(lastRecordType);
                                            attendanceRecordValidMapper.insertAttendanceRecordValid(aValid);
                                            nearTime = d2;
                                            //TODO 获取到一次有效打卡记录以后，分析打卡位置走势！！！（不然数据不准确）
//                                            log.info(aValid.toString());
                                        } else {
                                            nearNode = node;
                                        }
                                    }
                                }
                            }
                            //计算考勤状态
                            //考勤状态：   1:正常;2：迟到、3：早退，4:迟到还早退，9：未打卡
                            //考勤组合: 11全天正常；12：上午正常，下午迟到，13：早上正常，下午早退;14：上午正常，下午迟到还早退 19：上午正常，下午未打卡
                            //考勤组合 : 21：上午迟到，下午正常;22：全天迟到;23:上午迟到，下午早退;24:上午迟到，下午迟到+早退;29：上午迟到，下午未打卡
                            //考勤组合： 31：早上早退，下午正常；32：早上早退，下午迟到；33：全天早退；34：上午早退，下午迟到+早退 ；39：上午早退，下午未打卡
                            //考勤组合： 41：上午迟到还早退，下午正常；42:上午迟到还早退,下午迟到；43：上午迟到还早退,下午早退；44：全天迟到还早退；49：上午迟到还早退,下午未打卡
                            //考勤组合 : 91: 上午未打卡，下午正常 92：上午未打卡，下午迟到，93：上午未打卡，下午早退，94：上午未打卡，下午迟到还早退，99：全天未打卡
                            calValidClockInByRule(attendanceGroupUser, cardNo, groupMap, ruleMap, ymd, scBadge);
                        }
                    }
                }
            }
        }
        return resultVoMap;

    }


    /**
     * 保存打卡记录
     */
    @Override
    public void clearAttendanceResult(String hpdate) {
        //以班级为维度查询学生
        Long maxId = 0L;
        Integer pageSize = 100;
        int count = 0;
        Map<Long, AttendanceResultVo> resultVoMap = new HashMap<>();
        //查询所有考勤组
        List<AttendanceGroup> groupList = attendanceGroupMapper.selectAttendanceGroupList(new AttendanceGroup());
        //查询所有考勤规则
        List<AttendanceRule> ruleList = attendanceRuleMapper.selectAttendanceRuleList(new AttendanceRule());

        if (CollectionUtils.isNotEmpty(groupList) && CollectionUtils.isNotEmpty(ruleList)) {
            //把考勤组转成map
            Map<Long, AttendanceGroup> groupMap = groupList.stream().collect(Collectors.toMap(AttendanceGroup::getId, a -> a, (k1, k2) -> k1));
            //把考勤规则转成map
            Map<Long, AttendanceRule> ruleMap = ruleList.stream().collect(Collectors.toMap(AttendanceRule::getId, a -> a, (k1, k2) -> k1));
            while (true) {
                List<Student> studentList = this.studentMapper.getStudentByMaxId(maxId, pageSize);
                if (CollectionUtils.isEmpty(studentList)) {
                    return;
                }
                for (Student student : studentList) {
                    AttendanceResultVo attendanceResultVo = new AttendanceResultVo();
                    attendanceResultVo.setUserNo(student.getStudentNo());
                    attendanceResultVo.setSchoolId(student.getSchoolId());
                    attendanceResultVo.setUserName(student.getStudentName());
                    resultVoMap.put(student.getId(), attendanceResultVo);
                }
                count++;
                maxId = studentList.get(studentList.size() - 1).getId();
                log.info("最大id:{},处理次数:{}", maxId, count);
                List<Long> studentIdList = studentList.stream().map(student -> student.getId()).collect(Collectors.toList());
                //查询校徽
                List<ScBadge> scBadgeList = scBadgeMapper.selectScBadgeListByStudentIdList(studentIdList);
                if (CollectionUtils.isNotEmpty(scBadgeList)) {
                    //查询考勤组人员
                    List<AttendanceGroupUser> list = attendanceGroupUserMapper.selectAttendanceGroupUserListByUserIdList(studentIdList);
                    List<String> badgeNoList = scBadgeList.stream().map(badge -> badge.getBadgeNo()).collect(Collectors.toList());

                    Map<Long, List<ScBadge>> cardNoMap = scBadgeList.stream().collect(Collectors.groupingBy(ScBadge::getUserId));
                    //获取打卡记录
                    Map<String, Object> condition = new HashMap<String, Object>();
//                    badgeNoList.clear();
////                    badgeNoList.add("0971948096");
                    condition.put("cardNoList", badgeNoList);//校徽号就是考勤卡号
                    //todo 记得改成当天
//                    String dnow = DateUtils.dateTimeNow("yyyy-MM-dd");
                    String dnow = hpdate;
                    condition.put("createDate", dnow);
                    String l1 = DateUtils.parseDateToStr("yyyyMMdd", DateUtils.parseDate(dnow));
                    long ymd = Long.parseLong(l1);
                    AttendanceRecordValid tmp = new AttendanceRecordValid();
                    tmp.setYmd(ymd);
                    attendanceRecordValidMapper.deleteAttendanceRecordValidByObj(tmp);
                    List<AttendanceRecord> attendanceRecordList = this.attendanceRecordMapper.selectAttendanceRecordByCardNoListCreateDate(condition);
                    //按卡号分组
                    Map<String, List<AttendanceRecord>> recordMap = attendanceRecordList.stream().collect(Collectors.groupingBy(AttendanceRecord::getCardNo));
                    //TODO 第一次清洗数据，将符合一出一入或者一入一出的有效打卡数据，存入到有效记录表中
                    for (AttendanceGroupUser attendanceGroupUser : list) {
                        List<ScBadge> badgeList = cardNoMap.get(attendanceGroupUser.getUserId());
                        //1、获取校徽编号
                        String cardNo = "";
                        ScBadge scBadge = null;
                        if (CollectionUtils.isNotEmpty(badgeList)) {
                            scBadge = badgeList.get(0);
                            cardNo = scBadge.getBadgeNo();
                        }
                        //2、如果校徽编号不为空，则获取打卡记录，做数据清洗，将有效打卡记录写入记录表中
                        if (StringUtils.isNotEmpty(cardNo)) {
                            List<AttendanceRecord> recordList = recordMap.get(cardNo);
                            //如果打卡记录存在，按照打卡记录计算，否则不处理
                            if (CollectionUtils.isNotEmpty(recordList)) {
                                recordList = recordList.stream().sorted(Comparator.comparing(AttendanceRecord::getCreateTime)).collect(Collectors.toList());
                                AttendanceRecord nearNode = null;
                                Date nearTime = null;//最近一次打卡时间
                                int lastRecordType = -1;
                                for (int i = 0; i < recordList.size(); i++) {
                                    //如果设备Id相同，或者设备类型相同，则覆盖最近打卡记录
                                    if (nearNode == null) {
                                        nearNode = recordList.get(i);
                                        continue;
                                    }
                                    boolean bCome = isCome(nearNode.getDeviceNo());
                                    if (lastRecordType > -1) {
                                        //上一条的记录状态，如果是入，0，则bCome不可以是true
                                        if (lastRecordType == 0 && bCome) {
                                            nearNode = null;
                                            continue;
                                        }
                                        if (lastRecordType == 1 & !bCome) {
                                            nearNode = null;
                                            continue;
                                        }
                                    }
                                    AttendanceRecord node = recordList.get(i);
                                    if (nearTime != null) {
                                        //有过最近的打卡记录，需要去掉最近{设定的}时间端（系统默认5分钟）内的打卡记录
                                        long minute = DateUtil.between(node.getCreateTime(), nearTime, DateUnit.SECOND);
                                        if (minute < RECORD_TM) {
                                            continue;
                                        } else {
                                            nearNode = node;
                                            nearTime = null;
                                            continue;
                                        }
                                    }
                                    boolean bComeNode = isCome(node.getDeviceNo());
                                    if (bCome == bComeNode) {
                                        nearNode = node;
                                    } else {
                                        //不是同一类型设备
                                        //判断是否是乱入点，
                                        //如果是乱入点
                                        if(isLR(i,recordList,bComeNode)){
                                            continue;
                                        }
                                        //不是乱入点，则获取同批次最晚打卡点
                                        int index = getSameNodeLastOne(i,recordList,bComeNode);
                                        if(index>i){
                                            i = index;
                                            node = recordList.get(index);
                                            Date d2 = nearNode.getCreateTime();
                                            Date d1 = node.getCreateTime();
                                            AttendanceRecordValid aValid = new AttendanceRecordValid();
                                            aValid.setCardNo(cardNo);
                                            aValid.setHpdate(d2);
                                            aValid.setClockIn1(DateUtils.getHM(nearNode.getCreateTime()));
                                            aValid.setDeviceNo1(nearNode.getDeviceNo());
                                            aValid.setDeviceNo2(node.getDeviceNo());
                                            aValid.setClockIn2(DateUtils.getHM(node.getCreateTime()));
                                            aValid.setSchoolId(node.getSchoolId());
                                            lastRecordType = bComeNode ? CheckInEnum.OUT.getType() : CheckInEnum.IN.getType();
                                            aValid.setInOut(lastRecordType);
                                            attendanceRecordValidMapper.insertAttendanceRecordValid(aValid);
                                            nearTime = d1;
                                            nearNode = null;
                                        }
                                    }
                                }
                            }

                        }
                        //3、计算考勤
                        calValidClockInByRule(attendanceGroupUser, cardNo, groupMap, ruleMap, ymd, scBadge);
                    }
                }
            }
        }
    }


    /**
     * 判断奇数
     *
     * @param a
     * @return
     */
    public static boolean isCome(int a) {
        if ((a & 1) != 1) {   //是奇数 表示进
            return false;
        }
        return true;
    }


    /**
     * 判断奇数
     *
     * @param
     * @return
     */
    public static boolean isCome(String deviceNo) {
        //硬件是进口安置的设备
        List<String> comeDeviceNo = Lists.newArrayList("419598623", "2");
        boolean exist = comeDeviceNo.stream().anyMatch(name -> deviceNo.equals(name));
        if (exist) {
            return true;
        }
        return false;
    }

    /**
     *
     * @param i 记录开始项
     * @param recordList 数据记录
     * @param currCom 当前节点的出入方向
     * @return 是否是乱入点
     */
    public static boolean isLR(int i,List<AttendanceRecord> recordList,boolean currCom){
        int len = i+ LR_NUM;
        if(len>=recordList.size()){
           return true;
        }
        int vc = 0;
        for(int m=1;m<LR_NUM+1&&(len<recordList.size());m++){
            AttendanceRecord node1 = recordList.get(m+i);
            if(isCome(node1.getDeviceNo()) == currCom){
                vc++;
            }else{
                break;
            }
        }
        return vc!=2;
    }

    /**
     *
     * @param index
     * @param recordList
     * @param currCom
     * @return
     */
    public static int getSameNodeLastOne(int index,List<AttendanceRecord> recordList,boolean currCom){
        int rtn = index;
        AttendanceRecord currNode= recordList.get(index);
        for(int k = index+1;k<recordList.size();k++){
            AttendanceRecord tmp = recordList.get(k);
            if(isCome(tmp.getDeviceNo())==currCom){
                Date d1 = currNode.getCreateTime();
                Date d2 = tmp.getCreateTime();
                long tm_s = DateUtil.between(d2, d1, DateUnit.SECOND);
                if(tm_s<=RECORD_TM){
                    currNode = tmp;
                }else{
                    rtn = k-1;
                    break;
                }
            }else{
                if(isLR(k,recordList,isCome(tmp.getDeviceNo()))){
                    continue;
                }else{
                    rtn = k-1;
                    break;
                }
            }
        }
        return rtn;
    }

    /**
     * 考勤規則
     * 一进一出(第一次是进的记录--最后一次是出的记录)算是进入
     * 一出一进(第一次是出的记录--最后一次是进的记录)算是离校
     *
     * @param attendanceRuleList
     * @param resultVoMap
     * @param userId
     * @param attendanceRecordList
     * @param badge
     * @param save
     */
    private void calByRule(List<AttendanceRule> attendanceRuleList, Map<Long, AttendanceResultVo> resultVoMap, Long userId, List<AttendanceRecord> attendanceRecordList, ScBadge badge, boolean save) {
        LocalTime lastRuleEnd = LocalTime.of(0, 0, 0);//Long.valueOf(attendanceRuleList.get(attendanceRuleList.size()).getEndTime().replaceAll("[:]",""));;
        AttendanceResultVo attendanceResultVo = resultVoMap.get(userId);
        for (int i = 0; i < attendanceRuleList.size(); i++) {

            //3、查询考勤组对应的规则
            AttendanceRule rule = attendanceRuleList.get(i);
            //当前考勤规则开始时间
            // long ruleBegin = Long.valueOf(rule.getBeginTime().replaceAll("[:]",""));
            LocalTime ruleBegin = LocalTime.parse(rule.getBeginTime());
            // long ruleEnd = Long.valueOf(rule.getEndTime().replaceAll("[:]",""));
            LocalTime ruleEnd = LocalTime.parse(rule.getEndTime());

            //String now = DateUtils.dateTimeNow("HH:mm:ss");
            LocalTime now = LocalTime.now();
            log.info("开始计算学生{}-考勤规则[{}]-ruleBegin{}-ruleEnd{}-考勤记录是否存在{}", userId, rule.getName(), ruleBegin, ruleEnd, CollectionUtils.isNotEmpty(attendanceRecordList));
          /*  //如果当前时间未到考勤开始时间，那么结束计算
            if(now.isBefore(ruleBegin)){
                attendanceResultVo.setRemark((attendanceResultVo.getRemark()!=null?attendanceResultVo.getRemark():"") +rule.getName() + "未到考勤时间");
                continue;
            }*/

            Integer status = 0;
            if (attendanceRecordList == null || attendanceRecordList.size() == 0) {
                attendanceResultVo.setRemark((attendanceResultVo.getRemark() != null ? attendanceResultVo.getRemark() : "") + rule.getName() + "缺勤");
                status = 4;
                attendanceResultVo.setClockTime(null);
                saveClockInRecord(attendanceResultVo.getSchoolId(), attendanceResultVo.getBadgeNo(), attendanceResultVo.getClockTime(), badge, status, 0, save);
                continue;
            }
            //5、打卡记录X与规则进行比对，有以下几种情况
            Date minRecordTime = attendanceRecordList.get(0).getCreateTime();

            //提取离这个班次最近的考勤记录
            Long minRecord = null;
            /*
             * 如何找到连续记录的开始和结束位置
             * 1.判断下一个规则的开始时间是否大于打卡时间
             */
            Iterator<AttendanceRecord> recordIterator = attendanceRecordList.iterator();
            int index = 0;
            //当前规则第一条记录
            AttendanceRecord first = attendanceRecordList.get(0);
            long firstRecordTime = first.getCreateTime().getTime();
            AttendanceRecord last = first;
            while (recordIterator.hasNext()) {
                AttendanceRecord attendanceRecord = recordIterator.next();
                long currentRecordTime = attendanceRecord.getCreateTime().getTime();
                //当前打卡记录的时间超过开始时间十分钟 不做本次处理了（当做下个规则组来看）
                long minut = (currentRecordTime - firstRecordTime) / (1000 * 60);
                if ((currentRecordTime - firstRecordTime) > 1000 * 60 * 10) {
                    log.info("当前打卡记录的时间差{}【分分钟】超过开始时间十分钟 不做本次处理了（当做下个规则组来看）", minut);
                    break;
                } else {
                    //记录当前规则的最后一条数据 first和last 看做是一组操作 （一进一出 或者是一出一进）
                    //不满足的话就是非正常操作，说明当前学生逃课了
                    last = attendanceRecord;
                    recordIterator.remove();
                }
            }
            log.info("卡号{}打卡记录的最后一次打卡时间{},第一次打卡时间{}", first.getCardNo(), last.getCreateTime(), first.getCreateTime());
            //两个都是进的 说明进来了又出去了
            String desc = "";
            boolean come = true;
            int min = 0;
            if (isCome(first.getDeviceNo()) && isCome(last.getDeviceNo())) {
                attendanceResultVo.setClockTime(DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss", first.getCreateTime()));
                desc = "来了又出去了";//按缺勤计算
                come = true;
                attendanceResultVo.setRemark((attendanceResultVo.getRemark() != null ? attendanceResultVo.getRemark() : "") + rule.getName() + "缺勤-" + desc);
                status = 4;
            }
            if (isCome(first.getDeviceNo()) && !isCome(last.getDeviceNo())) {
                attendanceResultVo.setClockTime(DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss", first.getCreateTime()));
                desc = "来了";
                come = true;
                LocalTime firstTime = LocalTime.parse(DateUtils.parseDateToStr("HH:mm:ss", first.getCreateTime()), DateTimeFormatter.ofPattern("HH:mm:ss"));
                min = (int) ChronoUnit.MINUTES.between(firstTime, ruleBegin);
                log.info("时间差为----{}分钟,lastRuleEnd{},firstTime{},ruleEnd{}", min, lastRuleEnd, firstTime, ruleEnd);
                if (lastRuleEnd.isBefore(firstTime) && firstTime.isBefore(ruleEnd)) {
                    attendanceResultVo.setRemark((attendanceResultVo.getRemark() != null ? attendanceResultVo.getRemark() : "") + rule.getName() + "正常-" + desc);
                    status = 0;
                } else if (lastRuleEnd.isBefore(firstTime) && firstTime.isAfter(ruleEnd)) {
                    attendanceResultVo.setRemark((attendanceResultVo.getRemark() != null ? attendanceResultVo.getRemark() : "") + rule.getName() + "迟到-" + desc);
                    status = 1;
                } else if (lastRuleEnd.isAfter(firstTime)) {
                    attendanceResultVo.setRemark((attendanceResultVo.getRemark() != null ? attendanceResultVo.getRemark() : "") + rule.getName() + "缺勤-" + desc);
                    status = 4;
                }
            }
            if (!isCome(first.getDeviceNo()) && !isCome(last.getDeviceNo())) {
                attendanceResultVo.setClockTime(DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss", first.getCreateTime()));
                desc = "出去又进来了";
                come = false;
            }
            if (!isCome(first.getDeviceNo()) && isCome(last.getDeviceNo())) {
                attendanceResultVo.setClockTime(DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss", first.getCreateTime()));
                desc = "出去了";
                come = false;
            }

            //记录最后一次规则截止时间
            lastRuleEnd = ruleEnd;
            saveClockInRecord(attendanceResultVo.getSchoolId(), attendanceResultVo.getBadgeNo(), attendanceResultVo.getClockTime(), badge, status, min, save);


            //5.4 早上下班<存在X<下午上班，那么下午正常
            //5.5 下午上班<存在X<下午下班，那么下午迟到
            //5.6 最小值X>下午下班 或者 X不存在，那么下午缺勤
        }
    }

    /***
     *  计算考勤
     * @param attendanceGroupUser
     * @param cardNo
     * @param groupMap
     * @param ruleMap
     * @param ymd
     * @param scBadge
     */
    private void calValidClockInByRule(AttendanceGroupUser attendanceGroupUser, String cardNo, Map<Long, AttendanceGroup> groupMap, Map<Long, AttendanceRule> ruleMap, Long ymd, ScBadge scBadge) {
        //计算考勤状态
        //考勤状态： 1:正常;2：迟到、3：早退，4:迟到还早退，9：未打卡
        //考勤组合: 11全天正常；12：上午正常，下午迟到，13：早上正常，下午早退;14：上午正常，下午迟到还早退 19：上午正常，下午未打卡
        //考勤组合 : 21：上午迟到，下午正常;22：全天迟到;23:上午迟到，下午早退;24:上午迟到，下午迟到+早退;29：上午迟到，下午未打卡
        //考勤组合： 31：早上早退，下午正常；32：早上早退，下午迟到；33：全天早退；34：上午早退，下午迟到+早退 ；39：上午早退，下午未打卡
        //考勤组合： 41：上午迟到还早退，下午正常；42:上午迟到还早退,下午迟到；43：上午迟到还早退,下午早退；44：全天迟到还早退；49：上午迟到还早退,下午未打卡
        //考勤组合 : 91: 上午未打卡，下午正常 92：上午未打卡，下午迟到，93：上午未打卡，下午早退，94：上午未打卡，下午迟到还早退，99：全天未打卡
        if(StringUtils.isEmpty(cardNo)){
            return;
        }
        AttendanceGroup group = groupMap.get(attendanceGroupUser.getGroupId());
        ClockInRecord delClock = new ClockInRecord();
        delClock.setYmd(ymd);
        delClock.setBadgeNo(cardNo);
        clockInRecordService.deleteClockInRecordByObj(delClock);
        ClockInRecord clockInRecord = new ClockInRecord();
        clockInRecord.setSchoolId(scBadge.getScId());
        clockInRecord.setId(System.currentTimeMillis());
        clockInRecord.setUserId(scBadge.getUserId());
        clockInRecord.setBadgeNo(cardNo);
        clockInRecord.setYmd(ymd);
        int year = (int) (ymd / 10000);
        int mm = (int) (ymd % 10000 / 100);
        int d = (int) (ymd % 100);
        String hpdate = year + "-" + (mm <= 9 ? "0" + mm : mm) + "-" + (d <= 9 ? "0" + d : d);
        clockInRecord.setClockTime(DateUtils.parseDate(hpdate));
        if (group != null) {
            //按照group取出对应的规则
            String[] ruleIds = group.getRuleId().split(",");
            List<AttendanceRule> attendanceRuleList = new ArrayList<>();
            for (String ruleId : ruleIds) {
                attendanceRuleList.add(ruleMap.get(Long.valueOf(ruleId)));
            }
            AttendanceRecordValid itm = new AttendanceRecordValid();
            itm.setYmd(ymd);
            itm.setCardNo(cardNo);
            List<AttendanceRecordValid> validList = attendanceRecordValidMapper.selectAttendanceRecordValidList(itm);
            validList = validList.stream().sorted(Comparator.comparing(AttendanceRecordValid::getClockIn1)).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(validList)) {
                //有打卡记录
                if (CollectionUtils.isNotEmpty(attendanceRuleList)) {
                    int ruleId = 1;
                    for (AttendanceRule rule : attendanceRuleList) {
                        String bgTime = rule.getBeginTime();
                        String endTime = rule.getEndTime();
                        String offClazz = rule.getLastCourseTime();
                        bgTime = bgTime.replaceAll(":", "");
                        endTime = endTime.replaceAll(":", "");
                        if(StringUtils.isEmpty(offClazz)){
                            offClazz = "18:00";
                        }
                        offClazz = offClazz.replaceAll(":", "");
                        int inT = Integer.parseInt(bgTime);
                        int endT = Integer.parseInt(endTime);
                        int offC = Integer.parseInt(offClazz);


                        //分析数据区间，
                        int parseStart = inT - 100;//打卡开始的前1个小时
                        int parseEnd = offC + 100;//课程结束一个小时范围
                        for (AttendanceRecordValid valid : validList) {
                            Integer inhm = ruleId == 1 ? clockInRecord.getInHm1() : clockInRecord.getInHm2();
                            //当前有效打卡记录
                            //1、7:15---7:30，超过7.30算迟到，下课时间11:50
                            //1、13:30---14：00，超过7.30算迟到，下课时间18:00
                            int valTNear = valid.getClockIn2();
                            if (valTNear > parseEnd || valTNear < parseStart) {
                                continue;
                            }
                            if (valTNear <= endT) {
                                //正常打卡
                                if (inhm == null) {//尚未有打卡记录
                                    int m1 = (endT/100 - valTNear/100)*60+endT%100-valTNear%100;
                                    switch (ruleId) {
                                        case 1:
                                            clockInRecord.setInHm1(valTNear);
                                            clockInRecord.setMin1(m1);
                                            clockInRecord.setState1(1);
                                            break;
                                        case 2:
                                            clockInRecord.setInHm2(valTNear);
                                            clockInRecord.setMin2(m1);
                                            clockInRecord.setState2(1);
                                            break;
                                        case 3:
                                            clockInRecord.setInHm3(valTNear);
                                            clockInRecord.setMin3(m1);
                                            clockInRecord.setState3(1);
                                            break;
                                    }
                                } else {
                                    //提前出去了，应该请假了
                                    switch (ruleId) {
                                        case 1:
                                            clockInRecord.setOutHm1(valTNear);
                                            clockInRecord.setState1(3);
                                            break;
                                        case 2:
                                            clockInRecord.setOutHm2(valTNear);
                                            clockInRecord.setState2(3);
                                            break;
                                        case 3:
                                            clockInRecord.setOutHm3(valTNear);
                                            clockInRecord.setState3(3);
                                            break;
                                    }
                                }
                            }
                            if (valTNear > endT) {
                                //迟到。或者早退
                                if (inhm == null) {//尚未有打卡记录，说明时迟到了
                                    int m1 = (endT/100 - valTNear/100)*60+endT%100-valTNear%100;
                                    switch (ruleId) {
                                        case 1:
                                            clockInRecord.setInHm1(valTNear);
                                            clockInRecord.setMin1(m1);
                                            clockInRecord.setState1(2);
                                            break;
                                        case 2:
                                            clockInRecord.setInHm2(valTNear);
                                            clockInRecord.setMin2(m1);
                                            clockInRecord.setState2(2);
                                            break;
                                        case 3:
                                            clockInRecord.setInHm3(valTNear);
                                            clockInRecord.setMin3(m1);
                                            clockInRecord.setState3(2);
                                            break;
                                    }
                                } else {
                                    //提前出去了，应该时请假了
                                    boolean isNormalOff = false;
                                    if (valTNear >= offC) {
                                        //TODO 打卡时间和下课时间是一个时间点，是否应该算早退？？？？  正常下课
                                        isNormalOff = true;
                                    }
                                    switch (ruleId) {
                                        case 1:
                                            if (clockInRecord.getOutHm1() == null) {
                                                //出去的记录
                                                clockInRecord.setOutHm1(valTNear);
                                                if (!isNormalOff) {
                                                    clockInRecord.setState1(3);
                                                }
                                            } else {
                                                //出去的记录
                                                if (isNormalOff) {
                                                    clockInRecord.setOutHm1(valTNear);
                                                } else {
                                                    clockInRecord.setOutHm1(null);
                                                }
                                                int rhm = clockInRecord.getInHm1();
                                                if (rhm <= endT) {
                                                    clockInRecord.setState1(1);
                                                } else {
                                                    clockInRecord.setState1(2);
                                                }

                                            }
                                            break;
                                        case 2:
                                            if (clockInRecord.getOutHm2() == null) {
                                                //出去的记录
                                                clockInRecord.setOutHm2(valTNear);
                                                if (!isNormalOff) {
                                                    clockInRecord.setState2(3);
                                                }
                                            } else {
                                                //出去的记录
                                                if (isNormalOff) {
                                                    clockInRecord.setOutHm2(valTNear);
                                                } else {
                                                    clockInRecord.setOutHm2(null);
                                                }
                                                int rhm = clockInRecord.getInHm2();
                                                if (rhm <= endT) {
                                                    clockInRecord.setState2(1);
                                                } else {
                                                    clockInRecord.setState2(2);
                                                }
                                            }
                                            break;
                                        case 3:
                                            if (clockInRecord.getOutHm3() == null) {
                                                //出去的记录
                                                clockInRecord.setOutHm3(valTNear);
                                                if (!isNormalOff) {
                                                    clockInRecord.setState3(3);
                                                }
                                            } else {
                                                //出去的记录
                                                if (isNormalOff) {
                                                    clockInRecord.setOutHm3(valTNear);
                                                } else {
                                                    clockInRecord.setOutHm3(null);
                                                }
                                                int rhm = clockInRecord.getInHm3();
                                                if (rhm <= endT) {
                                                    clockInRecord.setState3(1);
                                                } else {
                                                    clockInRecord.setState3(2);
                                                }
                                            }
                                        break;
                                    }
                                }
                            }
                        }
                        ruleId++;
                    }
                } else {
                    if (ruleIds.length > 0) {
                        clockInRecord.setState1(1);
                        clockInRecord.setMin1(0);
                        clockInRecord.setStatus(1);
                    }
                    if (ruleIds.length > 1) {
                        clockInRecord.setState2(1);
                        clockInRecord.setMin2(0);
                        clockInRecord.setStatus(11);
                    }
                    if (ruleIds.length > 2) {
                        clockInRecord.setState3(1);
                        clockInRecord.setMin3(0);
                        clockInRecord.setStatus(111);
                    }
                }
            } else {
                if (ruleIds.length > 1) {
                    clockInRecord.setState1(9);
                    clockInRecord.setState2(9);
                    clockInRecord.setStatus(99);
                }
                if (ruleIds.length > 2) {
                    clockInRecord.setState3(9);
                    clockInRecord.setStatus(999);
                }
            }
        } else {
            clockInRecord.setState1(9);
            clockInRecord.setState2(9);
            clockInRecord.setState3(9);
            clockInRecord.setStatus(999);
        }
        clockInRecordService.insertClockInRecord(clockInRecord);
    }


    /**
     * 保存打卡记录
     *
     * @param schoolId
     * @param cardNo
     * @param deviceTime
     * @param scBadge
     * @param min
     */
    private void saveClockInRecord(Long schoolId, String cardNo, String deviceTime, ScBadge scBadge, Integer status, int min, boolean save) {
        if (save && scBadge != null) {
            ClockInRecord clockInRecord = new ClockInRecord();
            clockInRecord.setSchoolId(scBadge.getScId());
            clockInRecord.setBadgeNo(scBadge.getBadgeNo());
            clockInRecord.setClockTime(DateUtils.parseDate(deviceTime));
            clockInRecord.setStatus(status);
            clockInRecord.setUserNo(scBadge.getUserNo());
            clockInRecord.setUserType(scBadge.getUserType());
            clockInRecord.setUserId(scBadge.getUserId());
            clockInRecord.setUserName(scBadge.getUserName());
            clockInRecord.setInOutMinute(Math.abs(min));
            clockInRecordService.insertClockInRecord(clockInRecord);
        }
    }

    public static void main(String[] args) {
        //2020-11-12 07:38:52
        // long ruleBegin = Long.valueOf(rule.getBeginTime().replaceAll("[:]",""));
        long ruleBegin = DateUtils.dateTimeFormat("07:50:00", "HH:mm:ss").getTime();
        long ruleBegind = DateUtils.dateTimeFormat("08:00:00", "HH:mm:ss").getTime();
        long firstTime = DateUtils.dateTimeFormat(DateUtils.parseDateToStr("HH:mm:ss", DateUtils.addDays(new Date(), -12)), "HH:mm:ss").getTime();
        // System.out.printf((ruleBegind-ruleBegin)/1000/60+"");
        int min = (int) ((firstTime - ruleBegin) / 1000 / 60);
        // System.out.printf("firsttime"+firstTime+"date"+DateUtils.addDays(new Date(),-12));
        int hm = DateUtils.getHM(DateUtils.parseDate("2020-12-04 13:45:45"));
        System.out.println("HM: " + hm);
        LocalTime dateTime = LocalTime.now();
        System.out.println("d: " + dateTime.getNano());
        long mm = 20201204;
        System.out.println(mm / 10000);
        System.out.println(mm % 10000 / 100);
        System.out.println(mm % 100);
    }
}
